package transformer

import (
	"encoding/json"

	"github.com/project-flogo/core/activity"
	"github.com/project-flogo/core/data/coerce"
	"github.com/project-flogo/core/data/metadata"
	"github.com/tidwall/gjson"
	"github.com/tidwall/sjson"
)

type mapping struct {
	From string `md:"from" json:"from,omitempty"`
	To   string `md:"to" json:"to,omitempty"`
}

type settings struct {
	maps []mapping
	dump bool
}

type Activity struct {
	settings settings
}

func init() {
	_ = activity.Register(&Activity{}, New) //activity.Register(&Activity{}, New) to create instances using factory method 'New'
}

func New(ctx activity.InitContext) (activity.Activity, error) {
	sm := ctx.Settings()
	var s Settings
	err := metadata.MapToStruct(sm, &s, true)
	if err != nil {
		return nil, err
	}
	st := settings{}
	for _, m := range s.Maps {
		obj, err := coerce.ToObject(m)
		if err != nil {
			return nil, err
		}
		var mp mapping
		err = metadata.MapToStruct(obj, &mp, true)
		if err != nil {
			return nil, err
		}
		st.maps = append(st.maps, mp)
	}
	st.dump = s.Dump
	act := &Activity{
		settings: st,
	}
	return act, nil
}

var activityMd = activity.ToMetadata(&Settings{}, &Input{}, &Output{})

func (a *Activity) Metadata() *activity.Metadata {
	return activityMd
}

func (a *Activity) Eval(ctx activity.Context) (bool, error) {
	input := Input{}
	err := ctx.GetInputObject(&input)
	if err != nil {
		return true, err
	}
	bytes, err := json.Marshal(input.Data)
	if err != nil {
		return true, err
	}
	inData := string(bytes)
	outData := "{}"
	if a.settings.dump {
		outData = inData
	}
	for _, m := range a.settings.maps {
		got := gjson.Get(inData, m.From)
		var err error
		outData, err = sjson.SetRaw(outData, m.To, got.Raw)
		if err != nil {
			return true, err
		}
	}

	var data map[string]interface{}
	err = json.Unmarshal([]byte(outData), &data)
	if err != nil {
		return true, err
	}

	output := Output{}
	output.Data = data
	err = ctx.SetOutputObject(&output)
	return true, err
}
